#ifndef PFS_MUTEX_H
#define PFS_MUTEX_H
#include <pthread.h>
#include <semaphore.h>
namespace PFS
{
    class Mutex
    {
        public:
            Mutex()
            {
                pthread_mutexattr_init(&m_attr);
                pthread_mutexattr_settype(&m_attr, PTHREAD_MUTEX_RECURSIVE_NP);
                pthread_mutex_init(&m_mutex,&m_attr);
            }
            virtual ~Mutex()
            {
                pthread_mutexattr_destroy(&m_attr);
                pthread_mutex_destroy(&m_mutex);
            }
            int lock()
            {
                return pthread_mutex_lock(&m_mutex);
            }
            int unlock()
            {
                return pthread_mutex_unlock(&m_mutex);
            }
            int try_lock()
            {
                return pthread_mutex_trylock(&m_mutex);
            }
            pthread_mutex_t *get_mutex()
            {
                return (pthread_mutex_t *)&m_mutex;
            }
        private:
            pthread_mutex_t m_mutex;
            pthread_mutexattr_t m_attr;
    };
    class Condition
    {
        public:
            Condition()
            {
                this->m_mutex = NULL;
                pthread_cond_init(&m_cond,NULL);
            }
            virtual ~Condition()
            {
                pthread_cond_destroy(&m_cond);
            }
            void set_related_mutex(pthread_mutex_t* mutex)
            {
                m_mutex = mutex;
            }
            int wait()
            {
                return pthread_cond_wait(&m_cond,m_mutex);
            }
            int signal()
            {
                return pthread_cond_signal(&m_cond);
            }
            int broadcast()
            {
                return pthread_cond_broadcast(&m_cond);
            }
        private:
            pthread_mutex_t *m_mutex;
            pthread_cond_t m_cond;
    };
    class Semaphore
    {
        public:
            Semaphore()
            {
                sem_init(&m_sem,0,0);
            }
            Semaphore(int sem_num)
            {
                sem_init(&m_sem,0,sem_num);
            }
            virtual ~Semaphore()
            {
                sem_destroy(&m_sem);
            }
            int wait()
            {
                return sem_wait(&m_sem);
            }
            int post()
            {
                return sem_post(&m_sem);
            }
            int try_wait()
            {
                return sem_trywait(&m_sem);
            }
        private:
            sem_t m_sem;
    };

    enum LockMode
    {
        NO_PRIORITY,
        WRITE_PRIORITY,
        READ_PRIORITY
    };
    class RWLock
    {
        public:
            RWLock(LockMode lock_mode = NO_PRIORITY)
            {
                pthread_rwlockattr_t attr;
                pthread_rwlockattr_init(&attr);
                switch(lock_mode)
                {
                    case READ_PRIORITY:
                        pthread_rwlockattr_setkind_np(&attr,PTHREAD_RWLOCK_PREFER_READER_NP);
                        break;
                    case WRITE_PRIORITY:
                        pthread_rwlockattr_setkind_np(&attr,PTHREAD_RWLOCK_PREFER_WRITER_NONREURSIVE_NP);
                        break;
                    default:
                        break;
                }
                pthread_rwlock_init(&m_rwlock,&attr);
            }
            virtual ~RWLock()
            {
                pthread_rwlock_destroy(&m_rwlock);
            }
            int read_lock()
            {
                return pthread_rwlock_rdlock(&m_rwlock);
            }
            int write_lock()
            {
                return pthread_rwlock_wrlock(&m_rwlock);
            }
            int try_read_lock()
            {
                return pthread_rwlock_tryrdlock(&m_rwlock);
            }
            int try_write_lock()
            {
                return pthread_rwlock_trywrlock(&m_rwlock);
            }
            }
            int unlock()
            {
                return pthread_rwlock_unlock(&m_rwlock);
            }
        private:
            pthread_rwlock_t m_rwlock;
    };

    enum LockType
    {
        READ_LOCK,
        WRITE_LOCK
    };
    class ScopeRWLock
    {
        public:
            ScopeRWLock(RWLock& locker,const LockType lock_type)
                :m_locker(locker)
            {
                if(lock_type == READ_LOCK)
                    m_locker.read_lock();
                else
                    m_locker.write_lock();
            }
            ~ScopeRWLock()
            {
                m_locker.unlock();
            }
        private:
            RWLock& m_locker;
    };
}
#endif
